Une faille critique de sécurité a été trouvée dans la bibliothèque standard Rust. Elle ne peut être exploitée que sous Windows, à cause de la manière particulière dont le système de Microsoft découpe et interprète les arguments d’une commande.
La découverte a été faite par le chercheur en sécurité RyotaK, qui l’a signalée à l’équipe de développement de Rust. Elle est désormais référencée comme CVE-2024-24576 et a reçu la note maximale de sévérité, soit 10 sur 10. Elle a bien sûr reçu un petit nom pour l’occasion : BatBadBud.
Cette vulnérabilité est présente dans la bibliothèque standard de Rust, cette dernière n’échappant pas correctement les paramètres quand des commandes (batch ou cmd) sont invoquées.
En cas d’exploitation, le risque est que des commandes arbitraires soient transmises. Bien que la faille soit bien celle de Rust, elle remet sur le devant de la scène la manière dont Windows exécute les commandes et lit les arguments qui les accompagnent. En effet, comme expliqué par d’autres, Rust n’est pas le seul langage avec lequel il faut faire attention dans ce contexte.
Commentaires (34)
#1
#1.1
Alors non, le problème ne vient pas de Windows. Le problème vient de la différence de comportement entre le monde Unix et le monde Windows (plus particulièrement, les caractères d'échappement à utiliser lors de la création d'un processus, où, sous Linux les spawn, popen, etc. utilise le backslash tandis que CreateProcess (l'API WIndows donc) utilise le caret (^).
Le fait que la même erreur ait été faite par plusieurs équipes distinctes dans plusieurs langages n'en fait pas un problème de Windows. Quand on appelle mal une API, il ne faut pas rejeter la faute sur l'API.
Des comportements Unix ont été calqués sur Windows, et après on vient dire que c'est de la faute de Windows. Le problème aurait tout aussi bien pu être dans l'autre sens, et là, personne n'aurait remis en cause Linux.
Comme expliqué dans l'article, on peut regretter que la communication soit axée sur Rust alors que la faille touche de nombreux langages. C'est juste que la faille a été découverte via Rust en premier.
#1.2
Mais CreateProcess fonctionne différemment : https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
On passe une ligne de commande - pas un exécutable et ses paramètres - , ce qui introduit tout un tas de problèmes :
- celui des espaces
- celui des caractères spéciaux, qui t'en qu'à faire sont différents de Windows à Linux
- ... une limite de 32767 caractères
Pour reprendre Java, et je suppose que Rust fait pareil, on utilise bien des tableaux dans le code du langage : https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/ProcessBuilder.java#L1126
Côté Windows, on voit que Java recalcule une ligne de commande : https://github.com/openjdk/jdk/blob/master/src/java.base/windows/classes/java/lang/ProcessImpl.java#L453
Côté Linux c'est (bien) différent : on passe des tableaux, que des tableaux, rien que des tableaux avec la seule contrainte du caractère NUL en fin de chaîne.
- https://github.com/openjdk/jdk/blob/master/src/java.base/unix/classes/java/lang/ProcessImpl.java#L158
- https://github.com/openjdk/jdk/blob/master/src/java.base/unix/native/libjava/ProcessImpl_md.c#L655
Et pour le coup, si Windows ne fournit pas l'API pour éviter (car il s'agit de ça au final) de prendre les paramètres et de recréer une ligne de commande, alors oui Windows est responsable de la situation.
(si les liens ci-dessus référencent Java 21, ça existe depuis au moins Java 8, donc ce problème Windows qui concerne Rust, Java, etc.... est là depuis au moins 2013 :))
#1.5
Ben non, pas d'accord. En quoi c'est une situation à éviter ? Ce sont des choix, différents (ce que je souligne depuis le début), et c'est de cette différence que vient le problème.
Et les deux ont aussi des différences en dehors du caractère d'échappement. Il est beaucoup plus simple avec l'approche de Windows de vérifier la taille max des arguments de la ligne de commande (alors que sous linux, il faut la reconstituer, sans oublier de compter les espaces !). Car oui, il y a une taille max !
De même, l'approche utilisée par Windows préserve les espaces. S'il y a besoin de 3 espaces entre deux arguments, c'est possible de le faire sous Windows, pas sous Linux.
Encore une fois, il n'y a pas une approche qui soit meilleure qu'une autre. Ce sont juste 2 approches différentes.
#1.8
Parce que tu fournis une ligne de commande à analyser (parser) plutôt que de fournir le résultat de cette analyse ? Résultat que tu connais car au final, d'un point de vue programme tu connais les paramètres du programme que tu appelles.
Cela passe pour un shell, où l'utilisateur entre des commandes, mais pas pour des APIs bas niveau.
Et je ne vois pas le souci d'espaces dans la façon Linux : tu passes juste un tableau de chaîne de caractères (terminées par le caractère NUL). Le fait de vérifier la taille max de l'ensemble n'est pas plus coûteux : vu la différence de temps de lancement entre Linux et Windows (plus lent), ce n'est certainement pas ça le plus coûteux !
Si justement : dans le cas présent, il s'agit d’exécuter un programme depuis un autre programme avec les paramètres connus.
D'une part, la façon de faire de WIndows introduit des problèmes d'analyses vu que les règles sont difficiles à utiliser (je dis ça d'expérience de batch), pas documentée sur la doc de CreateProcess (pas trouvé sur la page de lien ou d’explication), d'autre part ça introduit des failles de sécurité du fait de cette complexité et de cas limites (notamment sur les caractères ").
Or, il s'agit bien de ça : Rust a une faille critique à cause de cette façon de faire qui nécessite un travail compliqué, mal compris et pour rien
#1.10
Cela passe pour un shell, où l'utilisateur entre des commandes, mais pas pour des APIs bas niveau.
La question est de savoir si les paramètres du ligne de commande sont nécessaires au système d'exploitation ou pas. La réponse est non. L'OS à juste besoin de savoir le programme à lancer, et les paramètres à passer, mais n'a pas à connaitre la structure des paramètres (tableau ou ligne).
Sous Linux, le choix a été fait d'organiser les paramètres sous forme de tableau. Sous Windows, c'est une chaine de caractère. Les deux sont tout à fait viable et ont de subtiles différences.
Je ne parlais de la complexité en terme de temps de calcul, mais de la complexité d'avoir un algorithme correcte. Et j'avoue ne pas trop voir en quoi le temps de lancement vient jouer un rôle ici. On parle des paramètres des programmes. Cela n'a strictement rien à voir.
Jusqu'au jour où on va se rendre compte que linux a en fait potentiellement le même problème, car quand un script est exécuté, l'interpréteur est déterminé via le shell bang. Chaque interpréteur peut avoir des règles d'échappements différents. Du coup, là aussi, on accusera Windows ?
Pas pour rien non. Quand les systèmes sont différents, il faut bien palier les différences. C'est le rôle du pattern adapteur ou d'un wrapper en programmation. Mais parfois c'est compliqué oui. Comme ici.
#1.16
Analyser une ligne de commande pour la traduire en paramètres, n'est pas le souci en soit : le souci c'est que cette analyse ne sert à rien dans le cas d'une API surtout quand tous les programmes C/C++ passent par une méthode main qui prends le nombre de paramètres (argc) et ces paramètres (argv).
C'est le cas autant sous Windows que sous Linux.
D'ailleurs, j'ai aussi trouvé la doc qui explique ce que fait Windows pour transformer une chaîne en paramètres :
https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170#the-main-function-signature
Justement : l'algorithme est simple.
Calculer la somme des tailles de chaîne d'un tableau c'est très facile.
Rien ne justifie la méthode de Windows, certainement pas la complexité algorithme ou le temps d’exécution de l'algorithme.
Sauf que les règles du shebang sont (probablement) entièrement liées au système : j'imagine que sous Linux, il commence déjà par voir à quoi il a affaire, programme ELF ou simple fichier avec shebang, puis il fait son travail : ce n'est pas l'application qui doit faire ce travail.
Si par exemple le caractères 0x36 doit être interdit, c'est le système que tu patches : pas chaque programme souhaitant lancer un programme.
Or, c'est justement ça le cœur du problème ici : Rust doit gérer ça, Java doit gérer ça, ...
Certes, mais tout le débat ici c'est justement que cette différence n'a aucun sens dans le cas présent et ne fait qu'introduire des failles de sécurité pour rien...
Si on veut parler d'un adapter/wrapper, probablement que tous les langages utilisant CreateProcess devraient passer par une API commune, cela réduirait les surfaces d'attaque.
#1.17
Et ça me donne l'impression qu'au final, il refait un exeve en ayant juste changer le programme pour celui du shebang et ajouter le paramètre optionnel en début du tableau = pas de ligne de commandes.
#1.20
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-winmain
Pas d'argc/argv...
#1.12
Dans le premier cas les paramètre sont simplement passés comme arguments à l’exécutable, un simple échappement suffit, alors que dans le second cas les paramètre sont interprétés comme une ligne de commande ce qui n'est clairement pas ce qu'attendent les utilisateurs, et qui est difficilement échappable.
On pourrait au moins excuser ce comportement étrange s'il était documenté.
#1.13
Cela n'en constitue pas pour autant une faille dans l'API Windows.
#1.14
Il n’empêche que, le fait que CreateProcess traite les arguments comme on traite une ligne de commande n'est ni le comportement auquel on s'attend, ni ce qui est documenté, et il en résulte une possibilité d'exploitation.
Dire si c'est une faille ou pas, c'est jouer sur les mots. En tout cas, ça en a les symptômes et ça devrait vraiment être corrigé, au minimum par une mise à jour de la documentation.
Historique des modifications :
Posté le 13/04/2024 à 10h31
Si tu parts du principe que l'utilisateur est responsable de la sécurité de ses paramètres mais qu'en même temps c'est l'implémentation qui fait foi et pas la documentation, en effet rien ne sera jamais une faille dans l'API.
Il n’empêche que pour moi, traiter les arguments de CreateProcess comme on traite une ligne de commande n'est clairement pas le comportement auquel on attend, d'autant plus si ça n'est pas documenté, et il en résulte une possibilité d'exploitation. Dire que ça n'est pas une faille, c'est jouer sur les mots : ça en a les symptômes.
Posté le 13/04/2024 à 10h32
Si tu parts du principe que l'utilisateur est responsable de la sécurité de ses paramètres mais qu'en même temps c'est l'implémentation qui fait foi et pas la documentation, en effet rien ne sera jamais une faille dans l'API.
Il n’empêche que pour moi, traiter les arguments de CreateProcess comme on traite une ligne de commande n'est clairement pas le comportement auquel on s'attend, d'autant plus si ça n'est pas documenté, et il en résulte une possibilité d'exploitation. Dire que ça n'est pas une faille, c'est jouer sur les mots : ça en a les symptômes.
Posté le 13/04/2024 à 10h34
Si tu parts du principe que l'utilisateur est responsable de la sécurité de ses paramètres mais qu'en même temps c'est l'implémentation qui fait foi et pas la documentation, en effet rien ne sera jamais une faille dans l'API.
Il n’empêche que pour moi, traiter les arguments de CreateProcess comme on traite une ligne de commande n'est clairement pas le comportement auquel on s'attend, d'autant plus si ça n'est pas documenté, et il en résulte une possibilité d'exploitation. Dire si c'est une faille ou pas, c'est jouer sur les mots. En tout cas, ça en a les symptômes et devrait être corrigé au minimum par une mise à jour de la documentation.
#1.21
#1.3
Vraiment ? L'argument serait valide pour un petit nombre de personnes, ou sur un langage unique.
Ce n'est pas le cas ici.
Le standard sur lequel s'appuie Rust est celui de GNU C, pas "Linux" (qui est un noyau). Rien de bien nouveau, et qui à défaut d'être une norme, est un standard d'interopérabilité.
Qui plus, un vieux standard : on ne parle pas de dernière fraîcheur.
La vieillerie
cmd.exe
contient une manière très particulière (comme personne d'autre) de fonctionner, et ce depuis donc bien trop longtemps.Une n-ème démonstration que l'habitude de M$ de produire du non-standard crée un/des risque(s) pour les utilisateurs de leurs produits.
Les langages doivent donc adopter un comportement particulier pour ce système d'exploitation particulier. D'aucuns dirait que c'est bien le système d'exploitation le problème.
Cf. https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html
Historique des modifications :
Posté le 12/04/2024 à 20h46
Vraiment ? L'argument serait valide pour un petit nombre de personnes, ou sur un langage unique.
Le standard sur lequel s'appuie Rust est celui de GNU C, pas "Linux" (qui est un noyau). Rien de bien nouveau, et à défaut d'être une norme, est un standard d'interopérabilité.
Qui plus, un vieux standard : on ne parle pas de dernière fraîcheur.
La vieillerie
cmd.exe
contient une manière très particulière (comme _personne_ d'autre) de fonctionner, et ce depuis donc bien trop longtemps.Une n-ème démonstration que l'habitude de M$ de produire du non-standard crée un/des risque(s) pour les utilisateurs de leurs produits.
Les langages doivent donc adopter un comportement particulier pour ce système d'exploitation particulier. D'aucuns dirait que c'est bien le système d'exploitation le problème.
Cf. https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html
Posté le 12/04/2024 à 20h47
Vraiment ? L'argument serait valide pour un petit nombre de personnes, ou sur un langage unique.
Ce n'est pas le cas ici.
Le standard sur lequel s'appuie Rust est celui de GNU C, pas "Linux" (qui est un noyau). Rien de bien nouveau, et à défaut d'être une norme, est un standard d'interopérabilité.
Qui plus, un vieux standard : on ne parle pas de dernière fraîcheur.
La vieillerie
cmd.exe
contient une manière très particulière (comme _personne_ d'autre) de fonctionner, et ce depuis donc bien trop longtemps.Une n-ème démonstration que l'habitude de M$ de produire du non-standard crée un/des risque(s) pour les utilisateurs de leurs produits.
Les langages doivent donc adopter un comportement particulier pour ce système d'exploitation particulier. D'aucuns dirait que c'est bien le système d'exploitation le problème.
Cf. https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html
Posté le 12/04/2024 à 20h47
Vraiment ? L'argument serait valide pour un petit nombre de personnes, ou sur un langage unique.
Ce n'est pas le cas ici.
Le standard sur lequel s'appuie Rust est celui de GNU C, pas "Linux" (qui est un noyau). Rien de bien nouveau, et qui à défaut d'être une norme, est un standard d'interopérabilité.
Qui plus, un vieux standard : on ne parle pas de dernière fraîcheur.
La vieillerie
cmd.exe
contient une manière très particulière (comme _personne_ d'autre) de fonctionner, et ce depuis donc bien trop longtemps.Une n-ème démonstration que l'habitude de M$ de produire du non-standard crée un/des risque(s) pour les utilisateurs de leurs produits.
Les langages doivent donc adopter un comportement particulier pour ce système d'exploitation particulier. D'aucuns dirait que c'est bien le système d'exploitation le problème.
Cf. https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html
#1.6
Qui plus, un vieux standard : on ne parle pas de dernière fraîcheur.
Pour être plus précis, le standard derrière s'appelle Posix (et pas GNU C). Sauf que l'API Windows existe 1985, POSIX depuis 1988. Windows ne respecte donc pas un standard qui n'existait tout simplement pas l'époque des premières versions du système d'exploitation.
C'est le principe de la bibliothèque standard de chaque langage. Quand tu as un langage dont l'API est proche de Posix, c'est plus simple à développer sur un système Posix que sur un système non Posix.
Le fait que plusieurs personnes se sont cassées les dents et qu'il y ait une faille à ce sujet aujourd'hui montre 2 choses :
- écrire une implémentation d'une API standard, ce n'est pas quelque chose de trivial
- un manque de documentation de l'API Windows.
Mais cela ne signifie pas que l'API présent une faille comme beaucoup le pense.
Le problème aujourd'hui touche uniquement Windows car :
- les systèmes à base de Linux (distribution classique, Android, etc.) respectent plus ou moins le standard POSIX
- les BSD respectent plus ou moins POSIX
- MacOS (basé sur BSD) respect plus ou moins POSIX
En fait, Windows est le seul système d'exploitation "grand publique" qui ne soit pas Posix de nos jours.
Comme dit plus haut, le standard n'existait pas à l'époque des premières versions de Windows.
#1.7
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'arguments sur toute la ligne, de la commande ou du script jusqu'à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Le fait que les langages de programmation aient repris cette sémantique indique bien que tout le monde considère ça comme la manière correcte de faire. Je ne connais aucun langage qui ait choisi de prendre ses arguments comme une seule grande chaîne de caractères au lieu d'une liste.
Historique des modifications :
Posté le 12/04/2024 à 22h36
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et fondamentalement difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Posté le 12/04/2024 à 22h37
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Posté le 12/04/2024 à 22h38
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Posté le 12/04/2024 à 22h40
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'argument de la ligne de commande à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Posté le 12/04/2024 à 22h41
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'argument de la ligne de commande à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Posté le 12/04/2024 à 22h42
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'arguments, de la ligne de commande ou du script jusqu'à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Posté le 12/04/2024 à 22h43
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'arguments sur toute la ligne, de la commande ou du script jusqu'à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Posté le 12/04/2024 à 22h46
Le problème fondamental est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'arguments sur toute la ligne, de la commande ou du script jusqu'à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Le fait que la plupart des langages aient repris cette sémantique indique bien que tout le monde considère ça comme la manière correcte de faire. Je ne connais aucun langage qui ait choisi de prendre ses arguments comme une seule grande chaîne de caractère au lieu d'une liste.
Posté le 12/04/2024 à 22h46
Le problème est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'arguments sur toute la ligne, de la commande ou du script jusqu'à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Le fait que les langages de programmation aient repris cette sémantique indique bien que tout le monde considère ça comme la manière correcte de faire. Je ne connais aucun langage qui ait choisi de prendre ses arguments comme une seule grande chaîne de caractères au lieu d'une liste.
#1.9
Quitte à me répéter, les premières versions de l'API Windows sont sorties avant pas mal de standardisation, dont celle du C (dont le premier standard remonte à 89 rappelons le). Si les points d'entrées ont été mis à jour depuis et que Windows accepte aussi le point d'entrée actuellement répandue (autrement dit, la fonction main), beaucoup des choix techniques de l'époque ont perduré pour des raisons de compatibilité (car oui, Windows possède une API extrêmement stable, contrairement à ce que l'on trouve dans les environnements Linux). Mais pareil, il n'y a pas de solution quoi meilleure que l'autre Les deux ont des avantages et des inconvénients.
Et une fois encore, ce n'est pas une limitation de Windows. C'est une problématique entre un environnement POSIX et un non POSIX. C'est radicalement différent.
Sérieusement ? Appeler ce qui relève d'un adapteur ou d'un wrapper un hack ? Sinon, certains langage n'avaient pas la notion de tableau d'arguments, mais bien d'une ligne : BASIC, Fortran à partir de 2003 (bon, qui propose les deux approchent :p), mais qui n'avait aucun mécanisme standard avant. Il me semble que COBOL permet les deux aussi. Et je suis loin de connaitre tous les langages.
Le problème est que tu considères la sémantique de la liste d'arguments comme allant de soi et étant une obligation aujourd'hui. Même si c'est très répandu, c'est loin d'être toujours le cas.
Ma correction.
Sinon, cette faille est du même acabit qu'une injection SQL qui serait exploitable car le connecteur à la base de données, sensé protéger via l'utilisation de requêtes paramétrées, fait mal son boulot. Est-ce qu'on dit pour autant que ce sont les SGBD qui sont troués ? Ou les API qui sont mal utilisées ?
#1.15
Ton analogie me fait penser à « magic_quotes », qu’on ne regrettera pas : une fonctionnalité pourrie, qu’il est impossible de faire marcher correctement.
Le comportement de windows est contre-intuitif et incohérent :
quand tu es dans ton programme, les arguments sont reçues sous forme de tableau de char
* quand tu appelles CreateProcess, les arguments doivent être fournis sous forme de ligne de commande
J’ai beaucoup de choses à reprocher au modèle fork/exec de posix, mais à minima il est cohérent dans la manière dont sont passés les arguments (qui date de bien avant C89, probablement avant C K&R, donc avant 73, bien avant windows).
Rajoute à ça que CreateProcess a un comportement différent sur les fichiers bat et les fichiers exe, et que si l’appelant ne précise pas l’extension il ne sait pas ce qui va être appelé, tu as une api qui est en réalité non prévisible.
Après, la responsabilité de la faille, c’est comme on veut. MS a la responsabilité d’avoir exposé une API pourrie (ce qui peut arriver), mais surtout de ne pas l’avoir remplacée par une API plus simple / prévisible depuis tout ce temps (c’est pas comme si l’api win32 était truffée de -Ex qui remplacent une api pourrie, conservée pour la compatibilité uniquement), ce qui est moins excusable.
#1.11
Cela n'aurait rien changé qu'il existe, par l'habitude constante précédemment mentionnée de l'entreprise éditrice… problème systématique étant la véritable cause racine de la faille sujet de l'article.
Historique des modifications :
Posté le 13/04/2024 à 05h16
Cela n'aurait rien changé qu'il existe, par l'habitude constante précédemment mentionnée de l'entreprise éditrice.
#1.18
Ce que tu dis n'est pas possible le noyau NT n'existe que depuis juillet 1993 et ne se base justement pas sur DOS, par contre, il gare une certaines compatibilités, mais les applications Ms-DOS/Windows de l'époque ne fonctionne pas sous Windows NT.
#1.19
Le changement de noyau NT reflétait surtout une modification profonde de la manière de communiquer avec le matériel. De manière très simpliste (car c'est loin d'être le seul changement mais sans doute le plus significatif), avant NT, les drivers pouvaient accéder directement au matériel (en by-passant complètement le noyau donc), ce qui n'est plus le cas avec NT.
Winapi, première version, date du 20 novembre 1985 d'après Wikipédia et existait bien avant le noyau NT.
Il ne faut pas confondre le noyau (qui gère entre autre le matériel) des API exposés pour les programmes.
#1.22
"To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file."
(doc de CreateProcess)
Je ne vois pas où est la partie qui dit que tu peux exécuter un bat... (ça parle d'exécuter un module)
#1.4
CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script. Les règles d’échappement sont différentes, mais il y a aussi le fait que les variables d’environnement, mots clé et caractères spéciaux du langage batch sont traitées ...
Alors oui on peut dire que c'est un choix de fonctionnement de Windows et Rust doit s'adapter à la situation. C'est d'ailleurs ce que Rust a fait au final, là où Java a considéré que ce n'était pas son problème. Mais pour faire cela bien dès le début, encore aurait il fallu que ce comportement ait été correctement documenté. Or la documentation MSDN actuelle de CreateProcess ne dit rien de l'utilisation implicite de cmd.exe pour l'exécution des fichiers de commande, au contraire, elle propose de lancer cmd.exe manuellement.
Il est difficile de reprocher à Rust de ne pas avoir anticipé un comportement non documenté d'une API Windows dont les conséquence sur la sécurité avaient échappé a la plupart jusqu'à présent.
Historique des modifications :
Posté le 12/04/2024 à 21h00
Le problème est que c'est plus compliqué que ça.
CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script (les règles d’échappement sont différentes, mais les variables d’environnement ou les mot clés du langage batch sont traitées).
Le gros du problème, c'est surtout que ce fonctionnement n'est pas clairement documenté par Microsoft, ce qui fait qu'il était difficile pour les développeurs de Rust de l'anticiper.
Posté le 13/04/2024 à 06h13
Le problème est plus compliqué que ça.
CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script (les règles d’échappement sont différentes, mais les variables d’environnement ou les mot clés du langage batch sont traitées).
Le gros du problème, c'est surtout que ce fonctionnement n'est pas clairement documenté par Microsoft, ce qui fait qu'il était difficile pour les développeurs de Rust de l'anticiper.
Posté le 13/04/2024 à 07h16
Le problème est plus compliqué que ça.
CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script. Les règles d’échappement sont différentes, mais il y a aussi le fait que les variables d’environnement, mots clé et caractères spéciaux du langage batch sont traitées ...
Alors on pourrait dire que Rust aurait du s'adapter à la situation, et c'est ce qu'il a fait (dans la mesure du possible), mais l'origine du problème, vient surtout du fait que ce fonctionnement n'est pas clairement documenté par Microsoft. Il est difficile de reprocher à Rust de ne pas avoir anticipé un comportement non documenté d'une API Windows.
Posté le 13/04/2024 à 07h26
Le problème est plus compliqué que ça.
CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script. Les règles d’échappement sont différentes, mais il y a aussi le fait que les variables d’environnement, mots clé et caractères spéciaux du langage batch sont traitées ...
Alors oui on pourrait dire que c'est un comportement connu de Windows et Rust aurait du s'adapter à la situation, et c'est ce qu'il a fait (dans la mesure du possible), mais encore faudrait il que ce comportement ait été correctement documenté, or la documentation MSDN actuelle de CreateProcess ne dit rien de l'utilisation implicite de cmd.exe pour l'exécution des fichiers de commande, au contraire, elle propose de lancer cmd.exe manuellement.
Il est difficile de reprocher à Rust de ne pas avoir anticipé un comportement non documenté d'une API Windows dont les conséquence sur la sécurité avaient échappé a la plupart jusqu'à présent.
Posté le 13/04/2024 à 07h30
Le problème est plus compliqué que ça.
CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script. Les règles d’échappement sont différentes, mais il y a aussi le fait que les variables d’environnement, mots clé et caractères spéciaux du langage batch sont traitées ...
Alors oui on pourrait dire que c'est un comportement connu de Windows et Rust aurait du s'adapter à la situation (ce qu'il a fait, là où Java a considéré que ce n'était pas son problème), mais pour l'anciciper, encore faudrait il que ce comportement ait été correctement documenté.
La documentation MSDN actuelle de CreateProcess ne dit rien de l'utilisation implicite de cmd.exe pour l'exécution des fichiers de commande, au contraire, elle propose de lancer cmd.exe manuellement. Il est difficile de reprocher à Rust de ne pas avoir anticipé un comportement non documenté d'une API Windows dont les conséquence sur la sécurité avaient échappé a la plupart jusqu'à présent.
Posté le 13/04/2024 à 07h36
Le problème est plus compliqué que ça.
CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script. Les règles d’échappement sont différentes, mais il y a aussi le fait que les variables d’environnement, mots clé et caractères spéciaux du langage batch sont traitées ...
Alors oui on peut dire que c'est un choix de fonctionnement de Windows et Rust doit s'adapter à la situation. C'est d'ailleurs ce que Rust a fait au final, là où Java a considéré que ce n'était pas son problème. Mais pour faire cela bien dès le début, encore aurait il fallu que ce comportement ait été correctement documenté. Or la documentation MSDN actuelle de CreateProcess ne dit rien de l'utilisation implicite de cmd.exe pour l'exécution des fichiers de commande, au contraire, elle propose de lancer cmd.exe manuellement. Il est difficile de reprocher à Rust de ne pas avoir anticipé un comportement non documenté d'une API Windows dont les conséquence sur la sécurité avaient échappé a la plupart jusqu'à présent.
#2
#2.1
Il ne faut jamais relâcher sa vigilance et surtout ne pas reposer sur les discours de ce type et les croyances. Même si le langage est conçu avec une gestion permettant d'éviter certaines erreurs, il y a toujours des risques.
Et si j'ai cette opinion, c'est parce qu'on entend encore de nos jours le poncif éculé et mensonger de "je suis sous Linux je suis invulnérable aux malwares" (même chose pour Mac, alors qu'il y a peu une faille des processeurs M1 a été dévoilée). La sécurité IT ne repose pas sur une marque mais un ensemble de pratiques. Le problème lorsqu'on s'engouffre dans ce genre de fantasme, c'est qu'on baisse son attention.
Historique des modifications :
Posté le 12/04/2024 à 17h50
C'est d'ailleurs pour cela que dans cet article lié j'avais émis une inquiétude quant au discours axé "sécurité" autour de ce langage.
Il ne faut jamais relâcher sa vigilance et ne pas reposer sur les discours de ce type et les croyances. Même si le langage est conçu avec une gestion permettant d'éviter certaines erreurs, il y a toujours des risques.
Et si j'ai cette opinion, c'est parce qu'on entend encore de nos jours le poncif éculé et mensonger de "je suis sous Linux je suis invulnérable aux malwares" (même chose pour Mac, alors qu'il y a peu une faille des processeurs M1 a été dévoilée). La sécurité IT ne repose pas sur une marque mais un ensemble de pratiques. Le problème lorsqu'on s'engouffre dans ce genre de fantasme, c'est qu'on baisse son attention.
#2.2
Donc même s'il n'est pas a l'abri de tous les problèmes, Rust reste clairement plus porté sur la sécurité que la majorité des langages.
Historique des modifications :
Posté le 12/04/2024 à 21h38
Pour le coup le problème est aussi en C et C++. Rust en a hérité car il utilise la fonction CreateProcess de l'API Windows en C et C++. Et Rust considère que c'est un problème et l'a corrigé immédiatement alors que le problème va semble il rester en l'état, au moins dans les langages en Java, C et C++.
Donc même s'il n'est pas a l'abri de tous les problèmes, Rust reste clairement plus porté sur la sécurité que la majorité des langage.
#2.3
D'où :
Choisir un langage apportant plus de sécurités fait donc parti de cet ensemble de pratiques. Auquel il faut ajouter la veille autour, et de ce qui est satellite. D'où mon exemple caricatural de "Linux c'est invulnérable aux malwares". Se reposer sur le discours de sécurité de Rust (que je n'ai jamais remis pas en cause, j'en suis incapable) est un risque en soit de réduire sa vigilance.
Exemple caricatural : le programme est plus sûr grace aux capacités du langage, tant mieux. Mais si la DB est exposée aux 4 vents, la sécurité est zéro pointé.
D'où, à nouveau :
Ma crainte au sujet du discours continu "Rust = plus sécurisé", c'est justement d'engendrer l'effet inverse.
#2.4
Je suis d'autant moins convaincu que la philosophie de Rust contrairement à des langages comme Java, n'est pas d'automatiser les problématiques de sécurité pour les oublier, mais d'aider à les relever et à les prendre en charge, ce qui pousse au contraire à en prendre conscience. Rust m'a poussé a m’intéresser à la sécurité plutôt que l'inverse.
Je suis beaucoup plus conscient des problèmes de sécurité, y compris dans les autres langages, depuis que j'ai appris le Rust, et pas forcément que sur ce qui touche à la mémoire, car la communauté Rust ne tend pas vraiment à se reposer uniquement sur ce qu'offre le langage. Il y a notamment déjà pas mal d'outils qui permettent d'aller bien au delà des garanties mémoire du langage. De ce que j'ai pu constater, il y a beaucoup plus d'attention aux problématiques de sécurité en général dans les communautés Rust, que dans les communautés C et C++. C'est probablement pas pour rien que le problème a été identifié en Rust alors qu'il concerne bien d'autre langages dont certains depuis bien plus longtemps.
Historique des modifications :
Posté le 13/04/2024 à 10h05
Je comprend l'idée, mais je n'adhère pas vraiment au discours qui dit qu'on apporte plus d'attention à la sécurité quand on doit tout gérer, en tout cas c'est pas du tout ce que je constate dans la pratique.
Je suis d'autant moins convaincu que la philosophie de Rust contrairement à des langages comme Java, n'est pas d'automatiser les problématiques de sécurité en les cachant mais d'aider à les relever et a les prendre en charge.
La communauté Rust ne tend pas vraiment a se reposer sur ce qu'offre le langage. Elle apporte beaucoup d'attention aux problématiques de sécurité, et il y a des outils qui permettent d'aller bien au delà des garanties mémoire du langage. C'est probablement pas pour rien que le problème a été identifié en Rust alors qu'il concerne bien d'autre langages dont certains depuis bien plus longtemps.
Posté le 13/04/2024 à 10h06
Je comprend l'idée, mais je n'adhère pas vraiment au discours qui dit qu'on apporte plus d'attention à la sécurité quand on doit tout gérer, en tout cas c'est pas du tout ce que je constate dans la pratique.
Je suis d'autant moins convaincu que la philosophie de Rust contrairement à des langages comme Java, n'est pas d'automatiser les problématiques de sécurité en les cachant mais d'aider à les relever et a les prendre en charge.
La communauté Rust ne tend pas vraiment a se reposer sur ce qu'offre le langage. Elle apporte beaucoup d'attention aux problématiques de sécurité, et il y a des outils qui permettent d'aller bien au delà des garanties mémoire du langage. C'est probablement pas pour rien que le problème a été identifié en Rust alors qu'il concerne bien d'autre langages dont certains depuis bien plus longtemps.
Posté le 13/04/2024 à 10h43
Je comprend l'idée, mais je n'adhère pas vraiment au discours qui dit qu'on apporte plus d'attention à la sécurité quand on doit tout gérer, en tout cas c'est pas du tout ce que je constate dans la pratique.
Je suis d'autant moins convaincu que la philosophie de Rust contrairement à des langages comme Java, n'est pas d'automatiser les problématiques de sécurité pour les oublier, mais d'aider à les re lever et a les prendre en charge. Je dirais que je suis beaucoup plus conscients des problèmes de sécurité depuis que j'ai appris le Rust, y compris dans les autres langages, et pas que sur ce qui touche à la mémoire.
La communauté Rust ne tend pas vraiment a se reposer uniquement sur ce qu'offre le langage. Elle apporte beaucoup d'attention aux problématiques de sécurité, et il y a des outils qui permettent d'aller bien au delà des garanties mémoire du langage. C'est probablement pas pour rien que le problème a été identifié en Rust alors qu'il concerne bien d'autre langages dont certains depuis bien plus longtemps.
Posté le 13/04/2024 à 10h49
Je comprend l'idée, mais je n'adhère pas vraiment au discours qui dit qu'on apporte plus d'attention à la sécurité quand on doit tout gérer, en tout cas c'est pas du tout ce que je constate dans la pratique.
Je suis d'autant moins convaincu que la philosophie de Rust contrairement à des langages comme Java, n'est pas d'automatiser les problématiques de sécurité pour les oublier, mais d'aider à les re lever et a les prendre en charge. Je dirais que je suis beaucoup plus conscients des problèmes de sécurité depuis que j'ai appris le Rust, y compris dans les autres langages, et pas que sur ce qui touche à la mémoire.
La communauté Rust ne tend pas vraiment à se reposer uniquement sur ce qu'offre le langage. Il y a notamment déjà pas mal d'outils qui permettent d'aller bien au delà des garanties mémoire du langage. Elle apporte beaucoup d'attention aux problématiques de sécurité, en tout cas bien plus que ce que je peux voir dans les communautés C et C++. C'est probablement pas pour rien que le problème a été identifié en Rust alors qu'il concerne bien d'autre langages dont certains depuis bien plus longtemps.
Posté le 13/04/2024 à 12h33
Je comprend l'idée, mais je n'adhère pas vraiment au discours qui dit qu'on apporte plus d'attention à la sécurité quand on doit tout gérer, en tout cas c'est pas du tout ce que je constate dans la pratique.
Je suis d'autant moins convaincu que la philosophie de Rust contrairement à des langages comme Java, n'est pas d'automatiser les problématiques de sécurité pour les oublier, mais d'aider à les relever et à les prendre en charge se qui pousse au contraire à en prendre conscience. Rust m'a poussé a m’intéresser à la sécurité plutôt que l'inverse.
Je suis beaucoup plus conscient des problèmes de sécurité, y compris dans les autres langages, depuis que j'ai appris le Rust, et pas forcément que sur ce qui touche à la mémoire, car la communauté Rust ne tend pas vraiment à se reposer uniquement sur ce qu'offre le langage. Il y a notamment déjà pas mal d'outils qui permettent d'aller bien au delà des garanties mémoire du langage. De ce que j'ai pu constater, il y a beaucoup plus d'attention aux problématiques de sécurité en général dans les communautés Rust, que dans les communautés C et C++. C'est probablement pas pour rien que le problème a été identifié en Rust alors qu'il concerne bien d'autre langages dont certains depuis bien plus longtemps.
#3
#3.1
Les .bat .cmd, et la construction de lignes de commandes sous Windows est un enfer. Utilisant souvent des scripts pour lancer des processus, via une ligne de commande construite, j'ai souvent eu des problèmes avec les caractères d'échappement et l'encodage.
Ca m'a même value de remplacer un exe de office chargé de lancer le bon exe car IE encodait le caractère d'échappement: même Ms s'y perd...
#4
C'est bien une faille Rust (ou autre langage) le portage n'a pas su adapter les spécificités de lancement des programmes et .bat de Windows.
Ça fait une mauvaise pub pour Rust mais Windows n'en sort pas grandit non plus...
#4.1
#4.2
#4.3